package io.task.cc.network;

import io.task.cc.network.queue_task.DecodeQueueTask;
import io.task.cc.network.queue_task.HandleQueueTask;
import io.task.cc.network.queue_task.SendQueueTask;
import io.task.cc.network.utils.lock.LockedSet;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.HashSet;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Slf4j
@Setter
@Getter
public abstract class ChannelContext {

    public static final String UNKNOWN_ADDRESS_IP = "$UNKNOWN";
    public boolean isReconnect = false;
    /**
     * 解码出现异常时，是否打印异常日志
     * 此值默认与org.tio.core.TioConfig.logWhenDecodeError保持一致
     */
    public boolean logWhenDecodeError = false;
    /**
     * 此值不设时，心跳时间取org.tio.core.TioConfig.heartbeatTimeout
     * 当然这个值如果小于org.tio.core.TioConfig.heartbeatTimeout，定时检查的时间间隔还是以org.tio.core.TioConfig.heartbeatTimeout为准，只是在判断时用此值
     */
    public Long heartbeatTimeout = null;
    /**
     * 一个packet所需要的字节数（用于应用告诉框架，下一次解码所需要的字节长度，省去冗余解码带来的性能损耗）
     */
    public Integer packetNeededLength = null;
    public Config config = null;
    public DecodeQueueTask decodeRunnable = null;
    public HandleQueueTask handleQueueRunnable = null;
    public SendQueueTask sendQueueRunnable = null;
    public final ReentrantReadWriteLock closeLock = new ReentrantReadWriteLock();
    private ReadCompletionHandler readCompletionHandler = null;                                            //new ReadCompletionHandler(this);
    public WriteCompletionHandler writeCompletionHandler = null;                                            //new WriteCompletionHandler(this);

    public String userid;
    private String token;
    private String bsId;
    public boolean isWaitingClose = false;
    public boolean isClosed = true;
    public boolean isRemoved = false;

    public boolean hasTempDir = false;
    public final ChannelStat channelStat = new ChannelStat();

    public AsynchronousSocketChannel asynchronousSocketChannel;
    private String id = null;
    private Node clientNode;
    private Node proxyClientNode = null;                                            //一些连接是代理的，譬如web服务器放在nginx后面，此时需要知道最原始的ip
    private Node serverNode;
    /**
     * 该连接在哪些组中
     */
    private LockedSet<String> groups = null;
    private Integer readBufferSize = null;                                            //个性化readBufferSize
    public CloseMeta closeMeta = new CloseMeta();
    private CloseCode closeCode = CloseCode.INIT_STATUS;                        //连接关闭的原因码


    public ChannelContext(Config config, AsynchronousSocketChannel asynchronousSocketChannel) throws IOException {
        super();
        init(config, asynchronousSocketChannel);


    }

    /**
     * 创建一个虚拟ChannelContext，主要用来模拟一些操作，譬如压力测试，真实场景中用得少
     */
    public ChannelContext(Config config) throws IOException {
        this(config, null);
    }


    /**
     * 创建Node
     */
    public abstract Node createClientNode(AsynchronousSocketChannel asynchronousSocketChannel) throws IOException;


    public Node getClientNode() {
        return clientNode;
    }

    public LockedSet<String> getGroups() {
        return groups;
    }


    public String getId() {
        return id;
    }


    public ReadCompletionHandler getReadCompletionHandler() {
        return readCompletionHandler;
    }

    public Node getServerNode() {
        return serverNode;
    }

    public String getToken() {
        return token;
    }


    public void init(Config config, AsynchronousSocketChannel asynchronousSocketChannel) throws IOException {
        id = UUID.randomUUID().toString();
        this.setTioConfig(config);
        config.ids.bind(this);
        this.setAsynchronousSocketChannel(asynchronousSocketChannel);
        this.readCompletionHandler = new ReadCompletionHandler(this);
        this.writeCompletionHandler = new WriteCompletionHandler(this);
        this.logWhenDecodeError = config.logWhenDecodeError;

        initOther();
    }

    void initOther() {
        {
            //在长连接中，绑定群组几乎是必须要干的事，所以直接在初始化时给它赋值，省得在后面做同步处理
            groups = new LockedSet<>(new HashSet<>());
        }
    }


    public void setAsynchronousSocketChannel(AsynchronousSocketChannel asynchronousSocketChannel) throws IOException {
        this.asynchronousSocketChannel = asynchronousSocketChannel;

        Node clientNode = createClientNode(asynchronousSocketChannel);
        setClientNode(clientNode);
    }


    public void setClientNode(Node clientNode) {
        {
            if (this.clientNode != null) {
                config.clientNodes.remove(this);
            }
        }

        this.clientNode = clientNode;


        if (this.clientNode != null && !Objects.equals(UNKNOWN_ADDRESS_IP, this.clientNode.getIp())) {
            config.clientNodes.put(this);
            //			clientNodeTraceFilename = StrUtil.replaceAll(clientNode.toString(), ":", "_");
        }
    }


    public void setClosed(boolean isClosed) {
        this.isClosed = isClosed;
        if (isClosed) {
            if (clientNode == null || !UNKNOWN_ADDRESS_IP.equals(clientNode.getIp())) {
                String before = this.toString();

                log.info("关闭前{}, 关闭后{}", before, this);
            }
        }
    }


    public void setTioConfig(Config config) {
        this.config = config;

        if (config != null) {
            decodeRunnable = new DecodeQueueTask(this, config.synThreadPoolExecutor);
            handleQueueRunnable = new HandleQueueTask(this, config.synThreadPoolExecutor);
            sendQueueRunnable = new SendQueueTask(this, config.synThreadPoolExecutor);
            config.connections.add(this);
        }
    }


    /**
     * 是否是服务器端
     */
    public abstract boolean isServer();

    public Integer getReadBufferSize() {
        if (readBufferSize != null && readBufferSize > 0) {
            return readBufferSize;
        }
        return this.config.getReadBufferSize();
    }


}
